from collections.abc import Iterable

from django.core.files import File
from rest_framework import serializers

from sysreptor import signals as sysreptor_signals
from sysreptor.pentests.import_export.serializers.common import (
    ExportImportSerializer,
    FileExportImportSerializer,
    MultiFormatSerializer,
)
from sysreptor.pentests.models import (
    FindingTemplate,
    FindingTemplateTranslation,
    Language,
    ReviewStatus,
    SourceEnum,
    UploadedTemplateImage,
)


class FindingTemplateImportSerializerV1(ExportImportSerializer):
    language = serializers.ChoiceField(choices=Language.choices, source='main_translation__language')
    status = serializers.CharField(source='main_translation__status', default=ReviewStatus.IN_PROGRESS)
    data = serializers.DictField(source='main_translation__data')

    class Meta:
        model = FindingTemplate
        fields = ['id', 'created', 'updated', 'tags', 'language', 'status', 'data']
        extra_kwargs = {'id': {'read_only': True}, 'created': {'read_only': False, 'required': False}}

    def create(self, validated_data):
        main_translation_data = {k[len('main_translation__'):]: validated_data.pop(k) for k in validated_data.copy().keys() if k.startswith('main_translation__')}
        template = FindingTemplate.objects.create(**{
            'source': SourceEnum.IMPORTED,
            'skip_post_create_signal': True,
        } | validated_data)
        data = main_translation_data.pop('data', {})
        main_translation = FindingTemplateTranslation(template=template, **main_translation_data)
        main_translation.update_data(data)
        main_translation.save()
        template.main_translation = main_translation
        template.save(update_fields=['main_translation'])
        sysreptor_signals.post_create.send(sender=template.__class__, instance=template)
        return template


class FindingTemplateTranslationExportImportSerializer(ExportImportSerializer):
    data = serializers.DictField(source='data_all')
    is_main = serializers.BooleanField()

    class Meta:
        model = FindingTemplateTranslation
        fields = ['id', 'created', 'updated', 'is_main', 'language', 'status', 'data']
        extra_kwargs = {'id': {'read_only': True}, 'created': {'read_only': False, 'required': False}}

    def create(self, validated_data):
        data = validated_data.pop('data_all', {})
        instance = FindingTemplateTranslation(**validated_data)
        instance.update_data(data)
        instance.save()
        return instance


class UploadedTemplateImageExportImportSerializer(FileExportImportSerializer):
    class Meta(FileExportImportSerializer.Meta):
        model = UploadedTemplateImage

    def get_linked_object(self):
        return self.context['template']

    def get_path_in_archive(self, name):
        # Get ID of old project_type from archive
        return str(self.context.get('template_id') or self.get_linked_object().id) + '-images/' + name


class FindingTemplateExportImportSerializerV2(ExportImportSerializer):
    translations = FindingTemplateTranslationExportImportSerializer(many=True, allow_empty=False)
    images = UploadedTemplateImageExportImportSerializer(many=True, required=False)

    class Meta:
        model = FindingTemplate
        fields = ['id', 'created', 'updated', 'tags', 'translations', 'images']
        extra_kwargs = {'id': {'read_only': False}, 'created': {'read_only': False, 'required': False}}

    def validate_translations(self, value):
        if len(list(filter(lambda t: t.get('is_main'), value))) != 1:
            raise serializers.ValidationError('No main translation given')
        if len(set(map(lambda t: t.get('language'), value))) != len(value):
            raise serializers.ValidationError('Duplicate template language detected')
        return value

    def to_representation(self, instance):
        self.context.update({'template': instance})
        return super().to_representation(instance)

    def export_files(self, instance) -> Iterable[tuple[str, File]]:
        self.context.update({'template': instance})
        imgf = self.fields['images']
        yield from imgf.export_files(instance=list(imgf.get_attribute(instance).all()))

    def create(self, validated_data):
        old_id = validated_data.pop('id')
        images_data = validated_data.pop('images', [])
        translations_data = validated_data.pop('translations')
        instance = FindingTemplate(**{
            'source': SourceEnum.IMPORTED,
            'skip_post_create_signal': True,
        } | validated_data)
        instance.save_without_historical_record()
        self.context['template'] = instance
        for t in translations_data:
            is_main = t.pop('is_main', False)
            translation_instance = self.fields['translations'].child.create(t | {'template': instance})
            if is_main:
                instance.main_translation = translation_instance
        instance._history_type = '+'
        instance.save()
        del instance._history_type

        self.context.update({'template': instance, 'template_id': old_id})
        self.fields['images'].create(images_data)

        sysreptor_signals.post_create.send(sender=instance.__class__, instance=instance)
        return instance


class FindingTemplateExportImportSerializer(MultiFormatSerializer):
    serializer_formats = {
        'templates/v2': FindingTemplateExportImportSerializerV2(),
        'templates/v1': FindingTemplateImportSerializerV1(),
    }
